home *** CD-ROM | disk | FTP | other *** search
- /*
- * hot.c - Scan an image for pixels with RGB values that will give
- * "unsafe" values of chrominance signal or composite signal
- * amplitude when encoded into an NTSC or PAL colour signal.
- * (This happens for certain high-intensity high-saturation colours
- * that are rare in real scenes, but can easily be present
- * in synthetic images.)
- *
- * Such pixels can be flagged so the user may then choose other
- * colours. Or, the offending pixels can be made "safe"
- * in a manner that preserves hue.
- *
- * There are two reasonable ways to make a pixel "safe":
- * We can reduce its intensity (luminance) while leaving
- * hue and saturation the same. Or, we can reduce saturation
- * while leaving hue and luminance the same. A #define selects
- * which strategy to use.
- *
- * Note to the user: You must add your own read_pixel() and write_pixel()
- * routines. You may have to modify pix_decode() and pix_encode().
- * MAXPIX, WID, and HGT are likely to need modification.
- */
-
- /*
- * Originally written as "ikNTSC.c" by Alan Wm Paeth,
- * University of Waterloo, August, 1985
- * Updated by Dave Martindale, Imax Systems Corp., December 1990
- */
-
- /*
- * Compile-time options.
- *
- * Define either NTSC or PAL as 1 to select the colour system.
- * Define the other one as zero, or leave it undefined.
- *
- * Define FLAG_HOT as 1 if you want "hot" pixels set to black
- * to identify them. Otherwise they will be made safe.
- *
- * Define REDUCE_SAT as 1 if you want hot pixels to be repaired by
- * reducing their saturation. By default, luminance is reduced.
- *
- * CHROMA_LIM is the limit (in IRE units) of the overall
- * chrominance amplitude; it should be 50 or perhaps
- * very slightly higher.
- *
- * COMPOS_LIM is the maximum amplitude (in IRE units) allowed for
- * the composite signal. A value of 100 is the maximum
- * monochrome white, and is always safe. 120 is the absolute
- * limit for NTSC broadcasting, since the transmitter's carrier
- * goes to zero with 120 IRE input signal. Generally, 110
- * is a good compromise - it allows somewhat brighter colours
- * than 100, while staying safely away from the hard limit.
- */
-
- #define NTSC 1
- #define PAL 0
- #define FLAG_HOT 0
- #define REDUCE_SAT 0
-
-
- #define CHROMA_LIM 50.0 /* chroma amplitude limit */
- #define COMPOS_LIM 110.0 /* max IRE amplitude */
-
- #if NTSC
- /*
- * RGB to YIQ encoding matrix.
- */
- double code_matrix[3][3] = {
- 0.2989, 0.5866, 0.1144,
- 0.5959, -0.2741, -0.3218,
- 0.2113, -0.5227, 0.3113,
- };
-
- #define PEDESTAL 7.5 /* 7.5 IRE black pedestal */
- #define GAMMA 2.2
- #endif NTSC
-
- #if PAL
- /*
- * RGB to YUV encoding matrix.
- */
- double code_matrix[3][3] = {
- 0.2989, 0.5866, 0.1144,
- -0.1473, -0.2891, 0.4364,
- 0.6149, -0.5145, -0.1004,
- };
-
- #define PEDESTAL 0.0 /* no pedestal in PAL */
- #define GAMMA 2.8
- #endif PAL
-
-
- #define SCALE 8192 /* scale factor: do floats with int math */
- #define MAXPIX 255 /* white value */
- #define WID 1024 /* FB dimensions */
- #define HGT 768
-
- typedef struct {
- unsigned char r, g, b;
- } Pixel;
-
- int tab[3][3][MAXPIX+1]; /* multiply lookup table */
- double chroma_lim; /* chroma limit */
- double compos_lim; /* composite amplitude limit */
- long ichroma_lim2; /* chroma limit squared (scaled integer) */
- int icompos_lim; /* composite amplitude limit (scaled integer) */
-
- double pix_decode(), gc(), inv_gc();
- int pix_encode(), hot();
-
- main()
- {
- Pixel p;
- int row, col;
-
- build_tab();
-
- for (col=0; col<WID; col++) {
- for(row=0; row<HGT; row++) {
- read_pixel(row, col, &p);
- if (hot(&p)) {
- write_pixel(row, col, &p);
- }
- }
- }
- }
-
- /*
- * build_tab: Build multiply lookup table.
- *
- * For each possible pixel value, decode value into floating-point
- * intensity. Then do gamma correction required by the video
- * standard. Scale the result by our fixed-point scale factor.
- * Then calculate 9 lookup table entries for this pixel value.
- *
- * We also calculate floating-point and scaled integer versions
- * of our limits here. This prevents evaluating expressions every pixel
- * when the compiler is too stupid to evaluate constant-valued
- * floating-point expressions at compile time.
- *
- * For convenience, the limits are #defined using IRE units.
- * We must convert them here into the units in which YIQ
- * are measured. The conversion from IRE to internal units
- * depends on the pedestal level in use, since as Y goes from
- * 0 to 1, the signal goes from the pedestal level to 100 IRE.
- * Chroma is always scaled to remain consistent with Y.
- */
-
- build_tab()
- {
- register double f;
- register int pv;
-
- for (pv = 0; pv <= MAXPIX; pv++) {
- f = SCALE * gc(pix_decode(pv));
- tab[0][0][pv] = (int)(f * code_matrix[0][0] + 0.5);
- tab[0][1][pv] = (int)(f * code_matrix[0][1] + 0.5);
- tab[0][2][pv] = (int)(f * code_matrix[0][2] + 0.5);
- tab[1][0][pv] = (int)(f * code_matrix[1][0] + 0.5);
- tab[1][1][pv] = (int)(f * code_matrix[1][1] + 0.5);
- tab[1][2][pv] = (int)(f * code_matrix[1][2] + 0.5);
- tab[2][0][pv] = (int)(f * code_matrix[2][0] + 0.5);
- tab[2][1][pv] = (int)(f * code_matrix[2][1] + 0.5);
- tab[2][2][pv] = (int)(f * code_matrix[2][2] + 0.5);
- }
-
- chroma_lim = (double)CHROMA_LIM / (100.0 - PEDESTAL);
- compos_lim = ((double)COMPOS_LIM - PEDESTAL) / (100.0 - PEDESTAL);
-
- ichroma_lim2 = (int)(chroma_lim * SCALE + 0.5);
- ichroma_lim2 *= ichroma_lim2;
- icompos_lim = (int)(compos_lim * SCALE + 0.5);
- }
-
- int
- hot(p)
- Pixel *p;
- {
- register int r, g, b;
- register int y, i, q;
- register long y2, c2;
- double pr, pg, pb;
- #if REDUCE_SAT
- double py;
- #endif
- register double fy, fc, t, scale;
- #if !FLAG_HOT
- static int prev_r = 0, prev_g = 0, prev_b = 0;
- static int new_r, new_g, new_b;
- #endif
- extern double pow(), hypot();
-
- r = p->r;
- g = p->g;
- b = p->b;
-
- /*
- * Pixel decoding, gamma correction, and matrix multiplication
- * all done by lookup table.
- *
- * "i" and "q" are the two chrominance components;
- * they are I and Q for NTSC.
- * For PAL, "i" is U (scaled B-Y) and "q" is V (scaled R-Y).
- * Since we only care about the length of the chroma vector,
- * not its angle, we don't care which is which.
- */
- y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
- i = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
- q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
-
- /*
- * Check to see if the chrominance vector is too long or the
- * composite waveform amplitude is too large.
- *
- * Chrominance is too large if
- *
- * sqrt(i^2, q^2) > chroma_lim.
- *
- * The composite signal amplitude is too large if
- *
- * y + sqrt(i^2, q^2) > compos_lim.
- *
- * We avoid doing the sqrt by checking
- *
- * i^2 + q^2 > chroma_lim^2
- * and
- * y + sqrt(i^2 + q^2) > compos_lim
- * sqrt(i^2 + q^2) > compos_lim - y
- * i^2 + q^2 > (compos_lim - y)^2
- *
- */
-
- c2 = (long)i * i + (long)q * q;
- y2 = (long)icompos_lim - y;
- y2 *= y2;
- if (c2 <= ichroma_lim2 && c2 <= y2) /* no problems */
- return 0;
-
- /*
- * Pixel is hot, choose desired (compilation time controlled) strategy
- */
- #if FLAG_HOT
- /*
- * Set the hot pixel to black to identify it.
- */
- p->r = p->g = p->b = 0;
- #else FLAG_HOT
- /*
- * Optimization: cache the last-computed hot pixel.
- */
- if (r == prev_r && g == prev_g && b == prev_b) {
- p->r = new_r;
- p->g = new_g;
- p->b = new_b;
- return 1;
- }
- prev_r = r;
- prev_g = g;
- prev_b = b;
-
- /*
- * Get Y and chroma amplitudes in floating point.
- *
- * If your C library doesn't have hypot(), just use
- * hypot(a,b) = sqrt(a*a, b*b);
- *
- * Then extract linear (un-gamma-corrected) floating-point
- * pixel RGB values.
- */
- fy = (double)y / SCALE;
- fc = hypot((double)i / SCALE, (double)q / SCALE);
-
- pr = pix_decode(r);
- pg = pix_decode(g);
- pb = pix_decode(b);
-
- /*
- * Reducing overall pixel intensity by scaling
- * R, G, and B reduces Y, I, and Q by the same factor.
- * This changes luminance but not saturation, since saturation
- * is determined by the chroma/luminance ratio.
- *
- * On the other hand, by linearly interpolating between the
- * original pixel value and a grey pixel with the same
- * luminance (R=G=B=Y), we change saturation without
- * affecting luminance.
- */
-
- #if !REDUCE_SAT
- /*
- * Calculate a scale factor that will bring the pixel
- * within both chroma and composite limits, if we scale
- * luminance and chroma simultaneously.
- *
- * The calculated chrominance reduction applies to the
- * gamma-corrected RGB values that are the input to
- * the RGB-to-YIQ operation. Multiplying the
- * original un-gamma-corrected pixel values by
- * the scaling factor raised to the "gamma" power
- * is equivalent, and avoids calling gc() and inv_gc()
- * three times each.
- */
- scale = chroma_lim / fc;
- t = compos_lim / (fy + fc);
- if (t < scale)
- scale = t;
- scale = pow(scale, GAMMA);
-
- r = pix_encode(scale * pr);
- g = pix_encode(scale * pg);
- b = pix_encode(scale * pb);
- #else REDUCE_SAT
- /*
- * Calculate a scale factor that will bring the pixel
- * within both chroma and composite limits, if we scale
- * chroma while leaving luminance unchanged.
- *
- * We have to interpolate gamma-corrected RGB values,
- * so we must convert from linear to gamma-corrected
- * before interpolation and then back to linear afterwards.
- */
- scale = chroma_lim / fc;
- t = (compos_lim - fy) / fc;
- if (t < scale)
- scale = t;
-
- pr = gc(pr);
- pg = gc(pg);
- pb = gc(pb);
- py = pr * code_matrix[0][0] + pg * code_matrix[0][1]
- + pb * code_matrix[0][2];
- r = pix_encode(inv_gc(py + scale * (pr - py)));
- g = pix_encode(inv_gc(py + scale * (pg - py)));
- b = pix_encode(inv_gc(py + scale * (pb - py)));
- #endif REDUCE_SAT
-
- p->r = new_r = r;
- p->g = new_g = g;
- p->b = new_b = b;
- #endif FLAG_HOT
- return 1;
- }
-
- /*
- * gc: apply the gamma correction specified for this video standard.
- * inv_gc: inverse function of gc.
- *
- * These are generally just a call to pow(), but be careful!
- * Future standards may use more complex functions.
- * (e.g. SMPTE 240M's "electro-optic transfer characteristic").
- */
-
- double
- gc(x)
- double x;
- {
- extern double pow();
-
- return pow(x, 1.0 / GAMMA);
- }
-
- double
- inv_gc(x)
- double x;
- {
- extern double pow();
-
- return pow(x, GAMMA);
- }
-
- /*
- * pix_decode: decode an integer pixel value into a floating-point
- * intensity in the range [0, 1].
- *
- * pix_encode: encode a floating-point intensity into an integer
- * pixel value.
- *
- * The code given here assumes simple linear encoding; you must change
- * these routines if you use a different pixel encoding technique.
- */
-
- double
- pix_decode(v)
- int v;
- {
- return (double)v / MAXPIX;
- }
-
- int
- pix_encode(v)
- double v;
- {
- return (int)(v * MAXPIX + 0.5);
- }
-
-